Spring Security 프로젝트 설정 5 - Security CORS 설정

✒️ 2025-05-28 14:23 내용 수정



흐름

spring_security_authentication_flow.png


Spring MVC CORS 설정

import lombok.RequiredArgsConstructor;  
import org.springframework.context.annotation.Configuration;  
import org.springframework.web.servlet.config.annotation.CorsRegistry;  
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;  
  
@Configuration  
@RequiredArgsConstructor  
public class WebMvcConfig implements WebMvcConfigurer {  
  
    @Override  
    public void addCorsMappings(CorsRegistry registry) {  
        registry.addMapping("/**")  
                .allowCredentials(true)  
                .allowedOrigins("http://localhost:3000")  
                .allowedMethods("OPTIONS", "GET", "POST", "PATCH", "DELETE");  
    }  
}

Spring Security CORS 설정 (CorsConfigurationSource)

@Configuration
@EnableWebSecurity
public class WebSecurityConfig {

	@Bean
	@Order(0)
	public SecurityFilterChain apiFilterChain(HttpSecurity http) throws Exception {
		http
			.securityMatcher("/api/**")
			.cors((cors) -> cors
				.configurationSource(apiConfigurationSource())
			)
			...
		return http.build();
	}

	@Bean
	@Order(1)
	public SecurityFilterChain myOtherFilterChain(HttpSecurity http) throws Exception {
		http
			.cors((cors) -> cors
				.configurationSource(myWebsiteConfigurationSource())
			)
			...
		return http.build();
	}

	// 첫 번째 필터에 사용되는 UrlBasedCorsConfigurationSource
	UrlBasedCorsConfigurationSource apiConfigurationSource() {
		CorsConfiguration configuration = new CorsConfiguration();
		configuration.setAllowedOrigins(Arrays.asList("https://api.example.com"));
		configuration.setAllowedMethods(Arrays.asList("GET","POST"));
		UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
		source.registerCorsConfiguration("/**", configuration);
		return source;
	}

	// 두 번째 필터에 사용되는 UrlBasedCorsConfigurationSource
	UrlBasedCorsConfigurationSource myWebsiteConfigurationSource() {
		CorsConfiguration configuration = new CorsConfiguration();
		configuration.setAllowedOrigins(Arrays.asList("https://example.com"));
		configuration.setAllowedMethods(Arrays.asList("GET","POST"));
		UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
		source.registerCorsConfiguration("/**", configuration);
		return source;
	}

}

SecurityConfig 수정

import jakarta.servlet.http.HttpServletRequest;  
import lombok.RequiredArgsConstructor;  
import org.springframework.context.annotation.Bean;  
import org.springframework.context.annotation.Configuration;  
import org.springframework.security.authentication.AuthenticationProvider;  
import org.springframework.security.config.annotation.web.builders.HttpSecurity;  
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;  
import org.springframework.security.config.http.SessionCreationPolicy;  
import org.springframework.security.web.SecurityFilterChain;  
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;  
import org.springframework.web.cors.CorsConfiguration;  
import org.springframework.web.cors.CorsConfigurationSource;  
  
import java.util.Collections;  
  
@Configuration  
@EnableWebSecurity  
@RequiredArgsConstructor  
public class SecurityConfig {  
  
    private final JwtAuthenticationFilter jwtAuthFilter;  
    private final AuthenticationProvider authenticationProvider;  
  
    @Bean  
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception{  
  
        http  
                // session stateless로 인해 꺼 둠  
                .csrf((auth)->auth.disable())  
                .authorizeRequests()  
                .requestMatchers("/api/v1/auth/**") // 나열된 요청들은  
                .permitAll() // 모두 허용  
                .anyRequest() // 그 외의 모든 요청은  
                .authenticated() // 인증 필요  
                .and()  
                .sessionManagement((session)->  
                        session // session state는 저장되면 안되므로 stateless로 설정  
                                .sessionCreationPolicy(SessionCreationPolicy.STATELESS))  
                .authenticationProvider(authenticationProvider)  
                .addFilterBefore(jwtAuthFilter,  
                        UsernamePasswordAuthenticationFilter.class); // jwt 필터 가동  
  
        // cors 설정  
        http.  
                cors((corsConfigurer)->  
                        corsConfigurer.configurationSource(corsConfigurationSource()));  
  
        return http.build();  
    }  
  
	// CORS 설정  
	@Bean  
	public CorsConfigurationSource corsConfigurationSource() {  
	    CorsConfiguration corsConfiguration = new CorsConfiguration();  
	  
	    // React client Origin을 허용  
	    corsConfiguration.setAllowedOrigins(Collections.singletonList("http://localhost:3000"));  
	    // React client로부터 오는 모든 메소드 허용  
	    corsConfiguration.setAllowedMethods(
		    Collections.singletonList(
			    "GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"
			)
		);  
	    // React client로부터 오는 credential(cookie) 허용  
	    corsConfiguration.setAllowCredentials(true);  
	    // React client로부터 오는 모든 헤더를 허용  
	    corsConfiguration.setAllowedHeaders(Collections.singletonList("*"));  
	    corsConfiguration.setMaxAge(3600L);  
	    // 클라이언트에 노출될 Authorization 헤더 설정
	    corsConfiguration.setExposedHeaders(Collections.singletonList("Authorization"));  
	  
	    // cors 설정을 URL에 매핑  
	    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();  
	    // 모든 경로에 대해 CORS 설정 적용  
	    source.registerCorsConfiguration("/**", corsConfiguration);  
	  
	    return source;  
	}
}

MvcConfig 추가

package com.ase.serverckecklist.security.config;  
  
import org.springframework.context.annotation.Configuration;  
import org.springframework.web.servlet.config.annotation.CorsRegistry;  
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;  
  
@Configuration  // bean 등록
@EnableWebMvc
public class CorsMvcConfig implements WebMvcConfigurer {  
  
    @Override  
    public void addCorsMappings(CorsRegistry registry) {  
        registry.addMapping("/**")  // mapping 설정
                .maxAge(500)  // 기간
                .allowedOrigins("http://localhost:3000")  // origin
                // 허용 메소드
                .allowedMethods("GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS");  
    }  
}
Access to XMLHttpRequest at 'http://localhost:9000/api/servers/' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.